Introduction
There never seems to be an end to the amazing lengths to which Liberty Basic can be pushed. Dennis comes in with another truly useful technique this month. He presents a set of functions to learn your default printer's page orientation and to change it to your desired setting. In the demo Dennis sets the page orientation to landscape. I have no doubt that these useful calls will become part of your library of got to have code snippets.
The Code
'Discovering and setting the printer page orientation.
'Demo by Dennis McKinney 2005.
'http://www.syberden.net/libertybelle
'http://www.syberden.net
'Developed using example code from MSDN.
'Function PrinterOrientation is free to use in your own applications.
nomainwin
Global True
True = 1
'Get the default printer name.
buf$ = space$(255)+chr$(0)
nSize = len(buf$)
calldll #kernel32, "GetProfileStringA", "windows" as ptr, "device" as ptr, "" as ptr, _
buf$ as ptr, nSize as long, r as long
prStr$ = left$(buf$, instr(buf$, chr$(0))-1)
PrnName$ = left$(prStr$, instr(prStr$, ",", 1)-1)+chr$(0)
open "winspool.drv" for dll as #winspool 'for printer functions
'Get current page orientaion.
bReturnOrientation = True
OrientationOld = PrinterOrientation(PrnName$, _NULL, bReturnOrientation)
'Set new page orientation, landscape mode in this case.
bReturnOrientation = False
Success = PrinterOrientation(PrnName$, _DMORIENT_LANDSCAPE, bReturnOrientation)
Select case Success
case True
lprint "Printed text"
dump
notice "SUCCESS!"
case False
Notice "Page orientation could not be set."
case -1
Notice PrName$+" is unable to change the page orientation or "+_
"does not support page orientation changes."
end select
'Be courteous and restore the original page orientation.
bReturnOrientation = False
Success = PrinterOrientation(PrnName$, OrientationOld, bReturnOrientation)
close #winspool
end
'***************************************************************
function PrinterOrientation(PrtName$, dmOrientation, bRetCurrentMode)
'Purpose #1:
' Set the paper orientation of printer PrtName$ to dmOrientation
' if bRetCurrentMode is False.
' Valid values for dmOrientation are either _DMORIENT_PORTRAIT
' or _DMORIENT_LANDSCAPE.
' Success returns True (1), failure returns False (0) or -1 if
' the printer does not support page orientation changes.
'Purpose #2: Return the current paper orientation of printer PrtName$
' if bRetCurrentMode is True. Argument dmOrientation is ignored.
' Success returns either _DMORIENT_PORTRAIT or _DMORIENT_LANDSCAPE.
' Failure returns 0.
struct pi2, _ 'PRINTER_INFO_2A
pServerName as ulong, _
pPrinterName as ulong, _
pShareName as ulong, _
pPortName as ulong, _
pDriverName as ulong, _
pComment as ulong, _
pLocation as ulong, _
pDevMode as ulong, _
pSepFile as ulong, _
pPrintProcessor as ulong, _
pDatatype as ulong, _
pParameters as ulong, _
pSecurityDescriptor as ulong, _
Attributes as ulong, _
Priority as ulong, _
DefaultPriority as ulong, _
StartTime as ulong, _
UntilTime as ulong, _
Status as ulong, _
cJobs as ulong, _
AveragePPM as ulong, _
struct dvmd, _ 'DEVMODE
dmDeviceName As Char[32],_
dmSpecVersion As Word,_
dmDriverVersion As Word,_
dmSize As Word,_
dmDriverExtra As Word,_
dmFields As Ulong,_
dmOrientation As Short,_
dmPaperSize As Short,_
dmPaperLength As Short,_
dmPaperWidth As Short,_
dmScale As Short,_
dmCopies As Short,_
dmDefaultSource As Short,_
dmPrintQuality As Short,_
dmColor As Short,_
dmDuplex As Short,_
dmYResolution As Short,_
dmTTOption As Short,_
dmCollate As Short,_
dmFormName As Char[32],_
dmLogPixels As Word,_
dmBitsPerPel As Ulong,_
dmPelsWidth As Ulong,_
dmPelsHeight As Ulong,_
dmDisplayFlags As Ulong,_
dmDisplayFrequency As Ulong
struct pd, _ 'PRINTER_DEFAULTSA
pDatatype As Ulong,_
pDevMode As Ulong,_
DesiredAccess As Ulong
struct pcbNeeded, x as ulong
PRINTER.ACCESS.USE = 8
PRINTER.ACCESS.ADMINISTER = 4
PRINTER.ALL.ACCESS = _STANDARD_RIGHTS_REQUIRED or PRINTER.ACCESS.ADMINISTER or PRINTER.ACCESS.USE
ERROR.INSUFFICIENT.BUFFER = 122
sizeofpd = len(pd.struct)
sizeofpi2 = len(pi2.struct)
sizeofdvmd = len(dvmd.struct)
'Initialize all elements of the structs to 0.
calldll #kernel32, "RtlZeroMemory", pd As Struct, sizeofpd as long, r as void
calldll #kernel32, "RtlZeroMemory", pi2 As Struct, sizeofpi2 as long, r as void
calldll #kernel32, "RtlZeroMemory", dvmd As Struct, sizeofdvmd as long, r as void
pcbNeeded.x.struct = 0
'Open printer handle (on Windows NT, you need full-access because you
'will eventually use SetPrinter)...
pd.DesiredAccess.struct = PRINTER.ALL.ACCESS
struct phPrinter, x as ulong
calldll #winspool, "OpenPrinterA", PrtName$ As Ptr, phPrinter As Struct, _
pd As struct, bFlag As Long
if not(bFlag) then exit function
hPrinter = phPrinter.x.struct
'The first GetPrinter tells you how big the buffer should be in
'order to hold all of PRINTER_INFO_2. Note that this should fail with
'ERROR_INSUFFICIENT_BUFFER. If GetPrinter fails for any other reason
'or pcbNeeded isn't set for some reason, then there is a problem...
calldll #kernel32, "SetLastError", 0 as long, r as void
calldll #winspool, "GetPrinterA", hPrinter as ulong, _
2 as long, 0 as long, 0 as long, pcbNeeded As Struct, bFlag as long
calldll #kernel32, "GetLastError", errNum as ulong
if (bFlag<>0) or (errNum <> ERROR.INSUFFICIENT.BUFFER) or (hPrinter = 0) then
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
exit function
end if
dwNeeded = pcbNeeded.x.struct
memType = _GMEM_FIXED or _GMEM_ZEROINIT '_GPTR
CallDll #kernel32, "GlobalAlloc", memType as long, dwNeeded as ulong, ppi2 as ulong
if ppi2 = 0 then
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
exit function
end if
'The second GetPrinter fills in all the current settings, so all you
'need to do is modify what you're interested in...
calldll #winspool, "GetPrinterA", hPrinter as ulong, _
2 as long, ppi2 as ulong, dwNeeded as ulong, pcbNeeded as struct, _
bFlag as long
if not(bFlag) then
CallDll #kernel32, "GlobalFree", ppi2 as ulong, r as long
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
exit function
end if
'Copy the info into our pi2 struct
CallDll #kernel32,"RtlMoveMemory", pi2 as struct, ppi2 as ulong, _
sizeofpi2 as long, ret as void
'If GetPrinter didn't fill in the DEVMODE, try to get it by calling
'DocumentProperties...
if pi2.pDevMode.struct = 0 then
calldll #spool, "DocumentPropertiesA", _NULL as ulong, hPrinter as ulong, _
PrtName$ As Ptr, _NULL as ulong, _NULL as ulong, 0 as long, dwNeeded as long
if dwNeeded <= 0 then
CallDll #kernel32, "GlobalFree", ppi2 as ulong, r as long
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
exit function
end if
CallDll #kernel32, "GlobalAlloc", memType as long, dwNeeded as ulong, pDevMode as ulong
if pDevMode = 0 then
CallDll #kernel32, "GlobalFree", ppi2 as ulong, r as long
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
exit function
end if
calldll #spool, "DocumentPropertiesA", _NULL as ulong, hPrinter as ulong, _
PrtName$ As Ptr, pDevMode as ulong, _NULL as ulong, _DM_OUT_BUFFER as long, _
lFlag as long
if (lFlag < 0) or (pDevMode = 0) then
CallDll #kernel32, "GlobalFree", pDevMode as ulong, r as long
CallDll #kernel32, "GlobalFree", ppi2 as ulong, r as long
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
exit function
end if
pi2.pDevMode.struct = pDevMode 'store memory address
end if
'Copy the pDevMode from memory into our dvmd struct
pAddress = pi2.pDevMode.struct
CallDll #kernel32,"RtlMoveMemory", dvmd as struct, pAddress as ulong, _
sizeofdvmd as long, ret as void
'If we are requesting the current mode...
if bRetCurrentMode then
PrinterOrientation = dvmd.dmOrientation.struct
CallDll #kernel32, "GlobalFree", ppi2 as ulong, r as long
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
if pDevMode then CallDll #kernel32, "GlobalFree", pDevMode as ulong, r as long
exit function
end if
'If the driver is reporting that it doesn't support this change...
if not(dvmd.dmFields.struct and _DM_ORIENTATION) then
CallDll #kernel32, "GlobalFree", ppi2 as ulong, r as long
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
if pDevMode then CallDll #kernel32, "GlobalFree", pDevMode as ulong, r as long
PrinterOrientation = -1
exit function
end if
'Specify exactly what we are attempting to change...
dvmd.dmFields.struct = _DM_ORIENTATION
dvmd.dmOrientation.struct = dmOrientation 'our function argument
'Put the dvmd back where it came from
CallDll #kernel32,"RtlMoveMemory", pAddress as ulong, dvmd as struct, sizeofdvmd as long, ret as void
'Do not attempt to set security descriptor...
pi2.pSecurityDescriptor.struct = _NULL
'Put the pi2 back where it came from
CallDll #kernel32,"RtlMoveMemory", ppi2 as ulong, pi2 as struct, sizeofpi2 as long, ret as void
'Make sure the driver-dependent part of devmode is updated...
fMode = _DM_OUT_BUFFER or _DM_IN_BUFFER
calldll #winspool, "DocumentPropertiesA", hWin as ulong, hPrinter as ulong, _
PrtName$ As Ptr, pDevMode as ulong, pDevMode as ulong, fMode as long, lFlag as long
if (lFlag < 0) then
CallDll #kernel32, "GlobalFree", ppi2 as ulong, r as long
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
if pDevMode then CallDll #kernel32, "GlobalFree", pDevMode as ulong, r as long
exit function
end if
'Update printer information...
calldll #winspool, "SetPrinterA", hPrinter as ulong, 2 as ulong, ppi2 as long, 0 as ulong, bFlag as long
if bFlag = 0 then 'The driver doesn't support it, or is unable to make the change...
CallDll #kernel32, "GlobalFree", ppi2 as ulong, r as long
calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
if pDevMode then CallDll #kernel32, "GlobalFree", pDevMode as ulong, r as long
exit function
end if
'Tell other apps that there was a change...
PrtName$ = PrtName$+chr$(0)
calldll #user32, "SendMessageTimeoutA", _HWND_BROADCAST as long, _
_WM_DEVMODECHANGE as long, 0 as long, PrtName$ as ptr, _SMTO_NORMAL as long, _
1000 as long, _NULL as long, r as long
'Clean up...
if ppi2 then CallDll #kernel32, "GlobalFree", ppi2 as ulong, r as long
if hPrinter then calldll #winspool, "ClosePrinter", hPrinter as ulong, r as long
if pDevMode then CallDll #kernel32, "GlobalFree", pDevMode as ulong, r as long
PrinterOrientation = 1 'Success
end function